#!/bin/bash
# shellcheck source=meta-facebook/meta-yosemite4/recipes-yosemite4/plat-tool/files/yosemite4-common-functions
source /usr/libexec/yosemite4-common-functions

is_nuvoton_board="$(check_nuvoton_board)"

enable_all_fan_input()
{
    # There is no fan_enable for 2nd source Fan IC: NCT7363
    if find /sys/bus/i2c/devices/*/hwmon/*/fan*_enable -print -quit 2>/dev/null | grep -q .; then
      for file in /sys/bus/i2c/devices/*/hwmon/*/fan*_enable
      do
        echo 1 > "${file}"
      done
    fi
}

set_all_fan_pwm_70_percent() {
    # 255 * 70% = 178.5
    pwm_value=178

    for pwm_node in /sys/bus/i2c/devices/*/hwmon/*/pwm*
    do
        if [[ ! "$pwm_node" =~ _enable ]]; then
            if echo $pwm_value > "$pwm_node"; then
                echo "Successfully set PWM for $pwm_node to $pwm_value"
            else
                echo "Failed to set PWM for $pwm_node"
            fi
        fi
    done
}

set_sd_bic_interface_to_i3c()
{
    if [ -z "$is_nuvoton_board" ]
    then
      # enable hot-join for I3C devices
      echo "1" > /sys/bus/i3c/devices/i3c-0/hotjoin
      echo "1" > /sys/bus/i3c/devices/i3c-1/hotjoin
    fi

    for host_bus in $(seq 0 7);
    do
      GPIOCHIP=$(basename /sys/bus/i2c/devices/"$host_bus"-0023/*gpiochip*)
      if [ "$GPIOCHIP" != "*gpiochip*" ]; then
        gpioset "$GPIOCHIP" 24=1
      fi
    done
}

# get present status of slot 1~8 and keep in /tmp/gpio/PRSNT_SB_SLOT{1~8}_N
check_slot_presence() {
    local ret

    mkdir -p /tmp/gpio
    for slot in {1..8}
    do
        echo "Checking presence status for slot ${slot}..."
        get_gpio PRSNT_SB_SLOT"${slot}"_N
        ret=$?
        if [ "$ret" -eq 0 ] || [ "$ret" -eq 1 ]; then
            echo "Valid status for slot ${slot}, ret = ${ret}, saving to file..."
            echo "$ret" > /tmp/gpio/PRSNT_SB_SLOT"${slot}"_N
        else
            echo "Invalid status for slot ${slot}, ret = ${ret}, skipping..."
        fi
    done
}

# Function to calculate BIT(n)
BIT() {
    echo $((1 << $1))
}

# Helper function to write to sysfs registers
write_to_i3c_hub_reg() {
    local offset=$1       # Register offset (e.g., 0x12 or 0x51)
    local value=$2        # Decimal value to write
    local hub=$3          # Hub (e.g., 0 or 1 for i3c-hub-0-* and i3c-hub-1-*)

    echo "$offset" > /sys/kernel/debug/i3c-hub-"${hub}"-*/reg/offset  # Set the register offset
    echo "$value" > /sys/kernel/debug/i3c-hub-"${hub}"-*/reg/access  # Write the decimal value
}

# Invert slot presence GPIO value (i: 0-base)
invert_slot_prsnt_gpio() {
    local i=$1
    local slot_i=$((i + 1))
    local file_path="/tmp/gpio/PRSNT_SB_SLOT${slot_i}_N"

    # Check if the file exists
    if [[ -f "$file_path" ]]; then
        local value
        value=$(cat "$file_path")
    else
        echo "Error: File $file_path does not exist"
        return 1
    fi

    # Validate and invert the value
    local r_gpio_val
    if [[ "$value" -eq 0 ]]; then
        r_gpio_val=1
    elif [[ "$value" -eq 1 ]]; then
        r_gpio_val=0
    else
        echo "Error: Invalid value in $file_path, expected 0 or 1 but got '$value'"
        return 2
    fi

    echo "$r_gpio_val"
}

# Compute and write combined values for hubs
set_I3C_hub() {
    for hub in 0 1; do
        local i3c_mask=0 smbus_mask=0 gpio_mask=0
        local i3c_val=0 smbus_val=0 gpio_val=0

        if [[ $hub -eq 0 ]]; then
            local start=0
            local end=3
        else
            local start=4
            local end=7
        fi

        for ((i=start; i<=end; i++)); do
            local slot_gpio_val
            slot_gpio_val=$(invert_slot_prsnt_gpio $i)

            local bit_pos=$((i - start))  # Adjust bit position to 0-3 for each hub
            i3c_mask=$((i3c_mask | $(BIT $bit_pos)))
            smbus_mask=$((smbus_mask | $(BIT $bit_pos)))
            gpio_mask=$((gpio_mask | $(BIT $bit_pos)))

            if [[ $slot_gpio_val -eq 1 ]]; then
                i3c_val=$((i3c_val | $(BIT $bit_pos)))
                smbus_val=$((smbus_val | $(BIT $bit_pos)))
            fi
            gpio_val=$((gpio_val | (slot_gpio_val << bit_pos)))

            local combined_mask=$((i3c_mask | smbus_mask | gpio_mask))
            local combined_val=$((i3c_val | smbus_val | gpio_val))
        done

        local combined_result=$((combined_val & combined_mask))
        local i3c_result=$((i3c_val & i3c_mask))

        write_to_i3c_hub_reg 16 105 "$hub"  # Unprotect register
        write_to_i3c_hub_reg 18 "$combined_result" "$hub"  # Write to reg 0x12 (combined values for hub)
        write_to_i3c_hub_reg 81 "$i3c_result" "$hub"  # Write to reg 0x51 (i3c values for hub)
        write_to_i3c_hub_reg 16 0 "$hub"  # Protect register
    done
}

# set initial value for GPIO output pins
set_gpio EN_P5V_USB_CPLD_R    1
set_gpio EN_NIC0_POWER_BMC_R  1
set_gpio EN_NIC1_POWER_BMC_R  1
set_gpio EN_NIC2_POWER_BMC_R  1
set_gpio EN_NIC3_POWER_BMC_R  1
set_gpio FM_BMC_RTCRST_R      0
set_gpio RST_USB_HUB_R_N      1
set_gpio FM_BMC_READY_R2      1
set_gpio SPI_LOCK_REQ_BMC_N   1
set_gpio EN_P3V_BAT_SCALED_R  0
set_gpio FM_BMC_SLED_CYCLE_R  0

set_gpio NIC0_MAIN_PWR_EN 1
set_gpio NIC1_MAIN_PWR_EN 1
set_gpio NIC2_MAIN_PWR_EN 1
set_gpio NIC3_MAIN_PWR_EN 1

if [ -z "$is_nuvoton_board" ]
then
    # short-term set gpio v0~v3 to input pin for slot5~8 reset button
    if [ ! -c "/dev/mem" ]; then
        mknod -m 660 /dev/mem c 1 1
    fi
    chown root:kmem /dev/mem
    devmem 0x1e78008c 32 0x19000000

    # adjust the i2c clock duty cycle for bus 11
    devmem 0x1E78A604 32 0x00487006
fi

check_slot_presence

# Enable all fan input as early as possible
enable_all_fan_input

# Set all fan PWM to 70%
set_all_fan_pwm_70_percent

handle_service_window

# Set interface to SD BIC to I3C for BMC
set_sd_bic_interface_to_i3c

# Set I3C hub register
set_I3C_hub

exit 0
